import os
from urllib.parse import quote_plus

from sqlalchemy.ext.asyncio import async_sessionmaker
from sqlalchemy.ext.asyncio.engine import AsyncEngine, create_async_engine
from sqlalchemy.ext.asyncio.session import AsyncSession

from app.config.logging import logger
from app.config.minio_storage_client import minio_storage_client
from app.core.mysql_alchemy import SQLAlchemyDataLayer
from app.decorator.timeit import timeit

DB_CONFIG = {
    "host": os.getenv("DATABASE_HOST"),
    "port": os.getenv("DATABASE_PORT"),
    "user": os.getenv("DATABASE_USER"),
    "password": os.getenv("DATABASE_PASSWORD"),
    "database": os.getenv("DATABASE_NAME"),
}

# 创建异步引擎
DATABASE_URL = f"mysql+aiomysql://{DB_CONFIG['user']}:{quote_plus(DB_CONFIG['password'])}@{DB_CONFIG['host']}:{DB_CONFIG['port']}/{DB_CONFIG['database']}"


def init_async_engine() -> AsyncEngine:
    return create_async_engine(
        url=DATABASE_URL,
        echo=os.getenv("DATABASE_ECHO_SQL", "false").lower() == "true",
        pool_size=int(os.getenv("DATABASE_POOL_MIN", 5)),
        max_overflow=int(os.getenv("DATABASE_POOL_MAX", 20)) - int(os.getenv("DATABASE_POOL_MIN", 5)),
        pool_pre_ping=True,  # 自动检查连接是否有效
        pool_recycle=3600,  # 每小时回收连接
    )


def init_session_maker(engine: AsyncEngine):
    return async_sessionmaker(
        bind=engine,
        class_=AsyncSession,
        expire_on_commit=False,
        expire_in=3600
    )


class DataBase:

    @timeit
    def __init__(self):
        # 异步引擎
        self.engine = init_async_engine()
        # db的异步session
        self.async_session = init_session_maker(self.engine)

    async def get_session(self):
        """
           获取异步数据库会话的依赖项
           使用示例: db: AsyncSession = Depends(get_db)
           """
        async with self.async_session() as session:
            try:
                yield session
                await session.commit()
            except Exception:
                await session.rollback()
                raise
            finally:
                await session.close()


# 初始化数据库
database = DataBase()

logger.info(f"mysql connection: {DATABASE_URL}")
# 初始化数据处理层
sql_alchemy_data_layer = SQLAlchemyDataLayer(conninfo=DATABASE_URL,
                                             # 必须配置该参数，因为sql_alchemy 中使用的双引号
                                             connect_args={
                                                 "sql_mode": "ANSI_QUOTES"
                                             },
                                             storage_provider=minio_storage_client,
                                             show_logger=True)
